#include "Swan.h"
#include "OCMain/zip/BZip2.h"
#include "VolumeGridData.h"
#include "OCMain/Log.h"

namespace OCPP
{
	namespace Volume
	{
		bool CSwan::readHeader(std::istream& sstream)
		{
			sstream.read((char*)&header, sizeof(swanheader));
			if (sstream.gcount() != sizeof(swanheader))
			{
				return false;
			}

			mImageS = header.XNumGrids;
			mImageT = header.YNumGrids;
			mImageR = header.ZNumGrids;

			size_t size = mImageS * mImageT * mImageR;
			mBuffer.resize(size);
			sstream.read((char*)&mBuffer[0], size);

			return true;
		}

		bool CSwan::convert(String fileName)
		{
			String name, ext, path;
			StringUtil::splitFullFilename(fileName, name, ext, path);
			StringUtil::toLowerCase(ext);

			if (ext == "bz2")
			{
				std::stringstream sstream;
				size_t size = BZip2::openBZ2(fileName, sstream);

				return convert(sstream);
			}

			std::ifstream ifs(fileName, std::ios::binary);
			return convert(ifs);
		}

		bool CSwan::convert(std::istream& sstream)
		{
			if (!readHeader(sstream))
			{
				return false;
			}

			int zStep = 500; // 0.5km

			int zNum = header.ZNumGrids;

			std::vector<std::pair<int, int> > heightMap;
			for (int z = 0; z < zNum; z++)
			{
				int hei1 = header.ZhighGrids[z] * 1000;
				if (z == 0)
				{
					heightMap.push_back(std::make_pair(-1, 0));
				}
				heightMap.push_back(std::make_pair(z, hei1));
				if (z < zNum - 1)
				{
					int hei2 = header.ZhighGrids[z + 1] * 1000;
					for (hei1 += zStep; hei1 < hei2; hei1 += zStep)
					{
						heightMap.push_back(std::make_pair(z, hei1));
					}
				}
			}

			int imageS = header.XNumGrids;
			int imageT = header.YNumGrids;
			int imageR = heightMap.size();
			int xySize = imageS * imageT;

			std::shared_ptr<Image> image = _CreateVolumeImage(imageS, imageT, imageR, PF_BYTE_RGB);

			for (int z = 0; z < imageR; z++)
			{
				int dataZ = heightMap[z].first;
				if (dataZ > -1 && dataZ < zNum)
				{
					long offset = dataZ * xySize;
					unsigned char* src = &mBuffer[offset];
					for (int r = 0; r < imageT; r++)
					{
						for (int c = 0; c < imageS; c++)
						{
							int i = r * imageS + c;
							uint8 v = src[i];
							unsigned char* dest = (unsigned char*)image->getData(c, imageT - 1 - r, z);
							dest[0] = v;
							if (v != 0 && c > 0 && r > 0 && r < imageT - 1 && c < imageS - 1)
							{
								uint8 px = src[i + 1];
								uint8 nx = src[i - 1];
								uint8 py = src[i + imageS];
								uint8 ny = src[i - imageS];

								Vector2 octVec = VolumeUtil::computeNormal(v, px, nx, py, ny);
								dest[1] = octVec.x;
								dest[2] = octVec.y;
							}
						}
					}
				}
			}

			float west = header.StartLon;
			float east = header.CenterLon * 2.0 - header.StartLon;
			float north = header.StartLat;
			float south = header.CenterLat * 2.0 - header.StartLat;
			getMeta().setSize(width_in_meter(south, east - west), height_in_meter(north - south), zStep * imageR);
			getMeta().setExtent(west, south, east, north);
			getMeta().setSource(imageS, imageT, imageR);
			getMeta().setSourceFormat(GL_RGB, GL_UNSIGNED_BYTE);
			setUint8Image(image);
			return true;
		}

		bool CSwan::extract(String fileName, const VolumeOptions& options)
		{
			String name, ext, path;
			StringUtil::splitFullFilename(fileName, name, ext, path);
			StringUtil::toLowerCase(ext);

			if (ext == "bz2")
			{
				std::stringstream sstream;
				size_t size = BZip2::openBZ2(fileName, sstream);

				if (!readHeader(sstream))
				{
					return false;
				}
			}
			else
			{
				std::ifstream ifs(fileName, std::ios::binary);
				if (!readHeader(ifs))
				{
					return false;
				}
			}

			int imageS = header.XNumGrids;
			int imageT = header.YNumGrids;
			int imageR = header.ZNumGrids;

			VolumeGridData gridData;
			String dateTime = str_format("%d%02d%02d%02d%02d00", header.year, header.month, header.day, header.hour, header.minute);
			gridData.addDimension(dateTime);
			gridData.getExtent().dimX = imageS;
			gridData.getExtent().dimY = imageT;
			gridData.getExtent().dimZ = imageR;
			gridData.getExtent().xMin = header.StartLon;
			gridData.getExtent().xMax = header.CenterLon * 2.0 - header.StartLon;
			gridData.getExtent().yMax = header.StartLat;
			gridData.getExtent().yMin = header.CenterLat * 2.0 - header.StartLat;
			gridData.getExtent().zMax = 19;
			gridData._vecChar.resize(imageS * imageT * imageR * 3);

			float minValue = 999.0f;
			float maxValue = -999.0f;

			int offset = 0;
			for (int z = 0; z < imageR; z++)
			{
				for (int y = 0; y < imageT; y++)
				{
					for (int x = 0; x < imageS; x++, offset++)
					{
						uint8 v = mBuffer[offset];
						minValue = std::min(minValue, float(v));
						maxValue = std::max(maxValue, float(v));
						int i = offset * 3;
						gridData._vecChar[i] = v;

						if (v != 0 && x > 0 && y > 0 && y < imageT - 1 && x < imageS - 1)
						{
							uint8 px = mBuffer[offset + 1];
							uint8 nx = mBuffer[offset - 1];
							uint8 py = mBuffer[offset + imageS];
							uint8 ny = mBuffer[offset - imageS];

							Vector2 octVec = VolumeUtil::computeNormal(v, px, nx, py, ny);
							gridData._vecChar[i + 1] = octVec.x;
							gridData._vecChar[i + 2] = octVec.y;
						}
					}
				}
			}

			OCPP_LOG_INFO << "minValue:" << minValue << " maxValue:" << maxValue << "\n";

			//return true;
			return options.getWriter().write(gridData, options);
		}
	}
}